#!/usr/bin/perl 

# What can I do?
# 1. Obtain titles of given URLs
# 2. DNS lookups for hostnames or IP addresses
# 3. Geolocation lookups for hostnames or IP addresses
# 4. Weather report for given cities
# 5. Dictionary lookups
# 6. Search Google for given keyword
# 7. Execute shell command
# 8. Execute one-line Perl script
# 9. Simple numerical calculation
# 10. Play music randomly by calling mplayer
# 11. Say beautiful poems from 《诗经》 

use strict;
use URI::Title qw(title);
use URI::Find::Simple qw(list_uris);
use Socket;
use IP::QQWry;
use Encode qw/encode decode/;
use Geo::IP::PurePerl;
#use Lingua::StarDict;
use Google::Search;
use Time::Out qw(timeout);
use File::Find;
use Term::ReadLine;

srand(time|$$);

my $key_word;
my @music_files;
my %content_of_shijing;

if (@ARGV) {
	my $input = "@ARGV";
	timeout 20 => sub {

		&print_help("non_interactive") if $ARGV[0] =~ /^h(elp)?\s*$/i;
		if (($key_word = $input) =~ s/^t(itle)?\s+//i){ 
			print "\n", &obtain_title($key_word), "\n";
		}

		&dns_lookup($key_word) if (($key_word = $input) =~ s/^dns\s+//i);
		&geolocate($key_word) if (($key_word = $input) =~ s/^l(ocate)?\s+//i);
		&weather_report($key_word) if (($key_word = $input) =~ s/^w(eather)?\s+//i);
		&dictionary_lookup($key_word) if (($key_word = $input) =~ s/^d(ictionary)?\s+//i);
		&google_search($key_word) if (($key_word = $input) =~ s/^g(oogle)?\s+//i);
		&numerical_calculate($key_word) if (($key_word = $input) =~ s/^c(alculate)?\s+//i);
		&say_poem($key_word) if (($key_word = $input) =~ s/^s(hijing)?\s*//i);

	};

	if ($@) {
		print "\nSorry, timeout reached!\n";
	}

	&play_music_randomly($key_word) if (($key_word = $input) =~ s/^m(usic)?\s*//i);

	print "\n";
	exit;
}

print "\n", "#" x 60, "\nHi! I am Tinker-Bot. So what can I do? Type \'h\' or \'help\' for\nmore information is strongly recommended!\nNote: you can type \"Tab\" for auto completion.\n", "#" x 60, "\n\n";

my $term = new Term::ReadLine 'Tinker-Bot';
my $attribs = $term->Attribs;
$attribs->{completion_entry_function} =	$attribs->{list_completion_function};
$attribs->{completion_word} = [ "title", "dns", "locate", "weather", "dictionary", "google", "perl", "calculate", "music", "shijing", "help", "quit" ];

my $prompt = "tinker> ";

while ( defined ($_ = $term->readline($prompt)) ) {

	eval {
		my $input = $_;
		timeout 16 => sub {
			# Exit under certain conditions
			last if $input =~ /(^\s*$)|(q(uit)?\s*)/i;
			&print_help("interactive") if $input =~ /^h(elp)?\s*$/i;

			if (($key_word = $input) =~ s/^t(itle)?\s+//i){ 
				print "\n", &obtain_title($key_word), "\n";
			}
		
			&dns_lookup($key_word) if (($key_word = $input) =~ s/^dns\s+//i);
			&geolocate($key_word) if (($key_word = $input) =~ s/^l(ocate)?\s+//i);
			&weather_report($key_word) if (($key_word = $input) =~ s/^w(eather)?\s+//i);
			&dictionary_lookup($key_word) if (($key_word = $input) =~ s/^d(ictionary)?\s+//i);
			&google_search($key_word) if (($key_word = $input) =~ s/^g(oogle)?\s+//i);
			print "\n", `$key_word` if (($key_word = $input) =~ s/^!\s*//i);
			&execute_perl_script($key_word) if (($key_word = $input) =~ s/^p(erl)?\s+//i);
			&numerical_calculate($key_word) if (($key_word = $input) =~ s/^c(alculate)?\s+//i);
			&say_poem($key_word) if (($key_word = $input) =~ s/^s(hijing)?\s*//i);

		};

		if ($@) {
			print "\nSorry, timeout reached!\n";
		}

		&play_music_randomly($key_word) if (($key_word = $input) =~ s/^m(usic)?\s*//i);

		print "\n";
	};

	print "\nError: $@\n" if $@;
}



###########################################################
############### Here go subroutines #######################
###########################################################

sub print_help {
	my $text = shift;

	if ($text eq "non_interactive") {
		print "\nUsage: $0 command <parameter(s)>\n";
	}elsif ($text eq "interactive") {
		print "\nUsage: command <parameter(s)>\n";
	}

	print "-" x 60, "\n01. \"t|title <URLs>\" for URLs' Title lookups\n02. \"dns <hostname|IP address>\" for DNS lookup\n03. \"l|locate <hostname|IP address>\" for geolocation lookup\n04. \"w|weather <city name>\" for weather report\n05. \"d|dictionary <words>\" to consult dictionaries\n06. \"g|google <keyword>\" for a Google Web Search\n07. \"!<command>\" to execute a shell command(interactive mode only)\n08. \"p|perl <one-line script>\" to execute the perl script(interactive mode only)\n09. \"c|calculate <expression>\" to calculate the exp's value\n10. \"m|music <n times(default 3)>\" to play music randomly\n11. \"s|shijing\" to print a poem from ShiJing\n12. \"h|help\" to print this usage\n13. \"q|quit\" or just press \"Enter\" key to quit\n"; 

}


sub obtain_title {		# Title lookups for given URLs
	my $text = shift;
	my $output = "";

	for (list_uris($text)) {
    	my $title = title("$_");
    	next unless defined $title;
        $output .= "[ $title ] ";
    }

	if (length($output) > 0) {
		$output = encode("utf8", $output);
		return "$output";
	}else{
		return "Sorry, no result found. Make sure that you've connected to the Internet!";
	}
}

sub dns_lookup {		# DNS lookups
	my $text = shift;

	# Get hostname by IP address
	if ($text=~ /(\d+\.)+\d+/) {
		my $addr = inet_aton($text);
        my @addr = gethostbyaddr($addr, AF_INET);

		if (length($addr[0]) > 0) {
			print "\n$text is $addr[0]\n" ;
		}else{
			print "\nSorry, no result found. Make sure that you've connected to the Internet!\n";
		}
	# Get IP address by hostname
	}else {
        my @addr = gethostbyname($text);
        my $straddr = inet_ntoa($addr[4]);

        if (length($straddr) > 0) {
			print "\n$text is $straddr\n" ;
		}else{
			print "\nSorry, no result found. Make sure that you've connected to the Internet!\n";
		}
	}
}

sub geolocate {
	(my $text = shift) =~ s/\s+//g;

	# Location lookups from QQWry
	my $dbfile = "/home/sapiens/tinker-bot/GeoIP/QQWry.dat";
	my $qqwry = IP::QQWry->new($dbfile);
	my $qqwry_version = $qqwry->db_version;
	$qqwry_version= encode("utf-8", decode("gbk", $qqwry_version));
	if ($qqwry_version =~ /.*?(\d+)年(\d+)月(\d+).*/) {
		my $tmp2 = length($2) < 2 ? "0$2" : $2;
		my $tmp3 = length($3) < 2 ? "0$3" : $3;
		$qqwry_version = $1 . $tmp2 . $tmp3;
	}

	my $output_qq = $qqwry->query($text);
	$output_qq = encode("utf-8", decode("gbk", $output_qq));

	# Location lookups from http://www.ip138.com
	(my $output_ip138 = `echo -e "ip=$text&action=2"|w3m -dump -no-cookie http://www.ip138.com/ips8.asp -post -|grep -P -o '(?<=本站主数据：).*|(?<=查询结果.：).*' 2>/dev/null`) =~ s/(^\s+)|(\s+$)//g;

	# Location lookups from GeoLiteCity
	my $gi = Geo::IP::PurePerl->open("/home/sapiens/tinker-bot/GeoIP/GeoLiteCity.dat", GEOIP_MEMORY_CACHE);
	my @data = $gi->get_city_record($text);
	(my $geo_version = $gi->database_info ) =~ s/.*LITE (\d+) Build.*/$1/;

	my $output = "";

	$output .= (length($output_qq) > 0) ? "QQWry(v$qqwry_version): $output_qq\n" : "QQWry: No result found\n";
	$output .= (length($output_ip138) > 0) ? "IP138: $output_ip138\n" : "IP138: No result found. Make sure that you've connected to the Internet!\n";
	$output .= @data ? "GeoLite(v$geo_version): @data[2,3,4]\n" : "GeoLite: No result found\n";
	
	print "\n$output";
}

sub weather_report {	# Weather forecast from Google Weather 
	my $text = shift;
	my @weather = `w3m -dump -no-cookie  http://www.google.cn/search?q=tq+$text |sed -n "/°C/,/北京市专业气象台/p"`;
	shift @weather;
	my $result = "";
	foreach (@weather) {
		chomp;
		s/(^\s*)|(\s*$)//;
		$result .= "$_ ";
	}
	if ($result =~ /^\s*$/) {
		$result = "Sorry, no result found. Make sure that you've connected to the Internet!";
	}else {
		$result =~ s/今日/今天/;
		$result =~ s/(今天|周\S+|北京)/\n$1/g;
		$result =~ s/(今天|周\S+) /$1：/g;
	}
	print "\n$result\n";
}

sub dictionary_lookup {
	my $word = shift;
#	my $sd = new Lingua::StarDict;
#	my $result = $sd->search($word);

#	foreach my $dict (@$result) {
#		foreach (keys %$dict) {
#			print "$_: ", ${$dict}{$_}, "\n";
#		}
#	}

	# sdcv is console version of StarDict program
	my $result = `sdcv $word 2>/dev/null`;
    if (length($result) > 0) {
		print $result;
	}else{
		print "\nSorry, no result found. Make sure that you've installed \"sdcv\"!\n";
	}
}

sub google_search {
	my $text = shift;
	
	# Input your Google AJAX Search API Key and referer website here.
	# If you havn't got one, go to http://code.google.com/apis/ajaxsearch/signup.html
	# to sign up and get a key.
	my $key = "******";		
	my $referer = "******";

	my $search = Google::Search->Web(q => $text, key => $key, referer => $referer);
    my $result = $search->first;
	my $i = 0;
	my $succeed = 0;
    while ($result) {
		(my $title = encode("utf8", $result->title) ) =~ s/<\/?b>//g;
        print $result->number + 1, " [ $title ]\n", $result->uri, "\n";
#       print $result->number, " ", obtain_title($result->uri), "\n", $result->uri, "\n";
        $result = $result->next;
		$succeed = 1;
		$i++;
		last if $i >= 10;	# Print top 10 search hits
    }
	if ($succeed == 0) {
		print "\nSorry, no result found. Make sure that you've connected to the Internet!\n";
	}

}

sub execute_perl_script {
	my $text = shift;
	open TMP_FILE, ">", "/tmp/tmp_script.pl" or die "Can't write to /tm/tmp_script.pl: $!";
	print TMP_FILE $text;
	close TMP_FILE;
	print "\n", `/usr/bin/perl /tmp/tmp_script.pl`, "\n";
	unlink "/tmp/tmp_script.pl";
}

sub numerical_calculate {
	my $text = shift;
	open TMP_FILE, ">", "/tmp/tmp_script.pl" or die "Can't write to /tm/tmp_script.pl: $!";
	print TMP_FILE "\$exp = $text;\nprint \$exp;";
	close TMP_FILE;
	print "\n", `/usr/bin/perl /tmp/tmp_script.pl 2>/dev/null`, "\n";
	unlink "/tmp/tmp_script.pl";
}

sub play_music_randomly {
	my $text = shift;
#	print "@music_files\n";

	find(\&get_music_file_list, "/home/sapiens/mus") unless @music_files;	# Replace the music pathname with your own here

	if ($text =~ /\d+/) {
		for (my $i=0; $i<$text; $i++) {
			 `mplayer $music_files[rand(@music_files)] > /dev/null 2>&1`; 
		}
	}else {
		for (my $i=0; $i<3; $i++) {
			 `mplayer $music_files[rand(@music_files)] > /dev/null 2>&1`; 
		}
	}

	sub get_music_file_list {
		if ((-f $_) and /(mp3|wma|ape|wav|ogg|dat)$/i) {
			(my $music_file = $File::Find::name) =~ s/ |\(|\)|\'/\\$&/g;
			push @music_files, $music_file;
		}
	}
}


sub say_poem {
	my $text = shift;
	my $title;

	my $file = "/home/sapiens/tinker-bot/shijing.txt";
	open SHIJING, "$file" or die "Can't open $file: $!";

	unless (defined( @{$content_of_shijing{"关雎"}} )) {
		while(<SHIJING>) {
			next if /(^\s*$)|(【注释】)|(【概要】)|\d+|(【译文】)|(第\S+节)|(本节包括)|(第\S+章)|(诗经)/;
			if (/第\S+篇\s*(\S+)/) {
				$title = $1;
				next;
			}
			s/(^\s*)|(\s*$)//g;
			push @{$content_of_shijing{$title}}, $_;
		}
	}

	close SHIJING;

	my @candidate;
	my @all;
	foreach (keys %content_of_shijing) {
		@candidate = (@candidate, grep(/$text/, @{$content_of_shijing{$_}} ) );
		@all = (@all, @{$content_of_shijing{$_}});
	}

	if (@candidate) {
		print "\n$candidate[rand(@candidate)]\n";
	}else {
		print "\n$all[rand(@all)]\n";
	}
}
